/*
 * Copyright 2014 NAVER Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ideal.ete.collector.controller;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;
import com.ideal.ete.collector.application.Application;
import com.ideal.ete.collector.applicationMap.ApplicationMap;
import com.ideal.ete.collector.applicationMap.MapWrap;
import com.ideal.ete.collector.ppLook.TreeNode;
import com.ideal.ete.collector.service.ApplicationFactory;
import com.ideal.ete.collector.service.MapService;
import com.ideal.ete.collector.service.ServiceTypeRegistryService;
import com.ideal.ete.collector.utils.Limiter;
import com.ideal.ete.collector.view.ApplicationTimeHistogramViewModel;
import com.ideal.ete.collector.vo.Range;
import com.ideal.ete.collector.vo.SearchOption;

/**
 * @author emeroad
 * @author netspid
 * @author jaehong.kim
 */
@Controller
public class MapController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private RestTemplate rs = new RestTemplate();

    @Autowired
    private MapService mapService;

    @Autowired
    private Limiter dateLimit;

    @Autowired
    private ServiceTypeRegistryService registry;

    @Autowired
    private ApplicationFactory applicationFactory;

    private static final String DEFAULT_SEARCH_DEPTH = "8";
    private static final int DEFAULT_MAX_SEARCH_DEPTH = 8;
    
    private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss S");


    /**
     * Server map data query within from ~ to timeframe
     *
     * @param applicationName
     * @param serviceTypeCode
     * @param from
     * @param to
     * @return
     */
    @RequestMapping(value = "/getServerMapData", method = RequestMethod.GET, params="serviceTypeCode")
    @ResponseBody
    public MapWrap getServerMapData(
                                    @RequestParam("applicationName") String applicationName,
                                    @RequestParam("serviceTypeCode") short serviceTypeCode,
                                    @RequestParam("from") long from,
                                    @RequestParam("to") long to,
                                    @RequestParam(value = "callerRange", defaultValue = DEFAULT_SEARCH_DEPTH) int callerRange,
                                    @RequestParam(value = "calleeRange", defaultValue = DEFAULT_SEARCH_DEPTH) int calleeRange) {
        final Range range = new Range(from, to);
        this.dateLimit.limit(range);

        SearchOption searchOption = new SearchOption(callerRange, calleeRange);
        assertSearchOption(searchOption);

        Application application = applicationFactory.createApplication(applicationName, serviceTypeCode);

        return selectApplicationMap(application, range, searchOption);
    	
    }
    @RequestMapping(value = "/getServerMapDataDetail", method = RequestMethod.GET, params="serviceTypeCode")
    @ResponseBody
    public String getServerMapDataDetail(
    		@RequestParam("applicationName") String applicationName,
    		@RequestParam("serviceTypeCode") short serviceTypeCode,
    		@RequestParam("from") long from,
    		@RequestParam("to") long to/*,
    		@RequestParam(value = "callerRange", defaultValue = DEFAULT_SEARCH_DEPTH) int callerRange,
    		@RequestParam(value = "calleeRange", defaultValue = DEFAULT_SEARCH_DEPTH) int calleeRange
    		*/) {

    	Map<String, Object> model = rs.getForObject("http://10.145.196.107:38080/getServerMapData.pinpoint?applicationName="
    			+ applicationName + "&serviceTypeCode=" + serviceTypeCode + "&from=" + from + "&to=" + to +"&callerRange=4&&calleeRange=4",
    			Map.class);
    	Map<String, Object> applicationMapData = (Map<String, Object>) model.get("applicationMapData");
    	if (CollectionUtils.isEmpty(applicationMapData)) {
    		return "";
    	}
    	List<Map<String,Object>> linkDataArray = (List<Map<String, Object>>) applicationMapData.get("linkDataArray");
    	List<Map<String,Object>> nodeDataArray = (List<Map<String, Object>>) applicationMapData.get("nodeDataArray");
    	
//    	System.out.println(linkDataArray);
    	
    	String str = JSON.toJSONString(applicationMapData);
    	
    	return str;
    }



    /**
     * Server map data query within from ~ to timeframe
     *
     * @param applicationName
     * @param serviceTypeName
     * @param from
     * @param to
     * @return
     */
    @RequestMapping(value = "/getServerMapData", method = RequestMethod.GET, params="serviceTypeName")
    @ResponseBody
    public MapWrap getServerMapData(
                                    @RequestParam("applicationName") String applicationName,
                                    @RequestParam("serviceTypeName") String serviceTypeName,
                                    @RequestParam("from") long from,
                                    @RequestParam("to") long to,
                                    @RequestParam(value = "callerRange", defaultValue = DEFAULT_SEARCH_DEPTH) int callerRange,
                                    @RequestParam(value = "calleeRange", defaultValue = DEFAULT_SEARCH_DEPTH) int calleeRange) {
        final Range range = new Range(from, to);
        this.dateLimit.limit(range);

        SearchOption searchOption = new SearchOption(callerRange, calleeRange);
        assertSearchOption(searchOption);

        Application application = applicationFactory.createApplicationByTypeName(applicationName, serviceTypeName);

        return selectApplicationMap(application, range, searchOption);
    }

    private MapWrap selectApplicationMap(Application application, Range range, SearchOption searchOption) {
        if (application == null) {
            throw new NullPointerException("application must not be null");
        }
        if (range == null) {
            throw new NullPointerException("range must not be null");
        }
        if (searchOption == null) {
            throw new NullPointerException("searchOption must not be null");
        }

        logger.info("getServerMap() application:{} range:{} searchOption:{}", application, range, searchOption);

        ApplicationMap map = mapService.selectApplicationMap(application, range, searchOption);
        
        return new MapWrap(map);
    }

    private void assertSearchOption(SearchOption searchOption) {
        int callerSearchDepth = searchOption.getCalleeSearchDepth();
        assertSearchDepth(callerSearchDepth, "invalid caller depth:" + callerSearchDepth);

        int calleeSearchDepth = searchOption.getCalleeSearchDepth();
        assertSearchDepth(searchOption.getCallerSearchDepth(), "invalid callee depth:" + calleeSearchDepth);
    }

    private void assertSearchDepth(int depth, String message) {
        if (depth < 0) {
            throw new IllegalArgumentException(message);
        }
        if (depth > DEFAULT_MAX_SEARCH_DEPTH) {
            throw new IllegalArgumentException(message);
        }
    }

    /**
     * Server map data query for the last "Period" timeframe
     *
     * @param applicationName
     * @param serviceTypeCode
     * @param period
     * @return
     */
    @RequestMapping(value = "/getLastServerMapData", method = RequestMethod.GET, params="serviceTypeCode")
    @ResponseBody
    public MapWrap getLastServerMapData(
                                        @RequestParam("applicationName") String applicationName,
                                        @RequestParam("serviceTypeCode") short serviceTypeCode,
                                        @RequestParam("period") long period,
                                        @RequestParam(value = "callerRange", defaultValue = DEFAULT_SEARCH_DEPTH) int callerRange,
                                        @RequestParam(value = "calleeRange", defaultValue = DEFAULT_SEARCH_DEPTH) int calleeRange) {

        long to = TimeUtils.getDelayLastTime();
        long from = to - period;

        final Range range = new Range(from, to);
        this.dateLimit.limit(range);

        SearchOption searchOption = new SearchOption(callerRange, calleeRange);
        assertSearchOption(searchOption);

        Application application = applicationFactory.createApplication(applicationName, serviceTypeCode);

        return selectApplicationMap(application, range, searchOption);
    }

    /**
     * Server map data query for the last "Period" timeframe
     *
     * @param applicationName
     * @param serviceTypeName
     * @param period
     * @return
     */
    @RequestMapping(value = "/getLastServerMapData", method = RequestMethod.GET, params="serviceTypeName")
    @ResponseBody
    public MapWrap getLastServerMapData(
                                        @RequestParam("applicationName") String applicationName,
                                        @RequestParam("serviceTypeName") String serviceTypeName,
                                        @RequestParam("period") long period,
                                        @RequestParam(value = "callerRange", defaultValue = DEFAULT_SEARCH_DEPTH) int callerRange,
                                        @RequestParam(value = "calleeRange", defaultValue = DEFAULT_SEARCH_DEPTH) int calleeRange) {

        long to = TimeUtils.getDelayLastTime();
        long from = to - period;

        final Range range = new Range(from, to);
        this.dateLimit.limit(range);

        SearchOption searchOption = new SearchOption(callerRange, calleeRange);
        assertSearchOption(searchOption);

        Application application = applicationFactory.createApplicationByTypeName(applicationName, serviceTypeName);
        return selectApplicationMap(application, range, searchOption);
    }

    @RequestMapping(value = "/getResponseTimeHistogramData", method = RequestMethod.GET, params = "serviceTypeName")
    @ResponseBody
    public ApplicationTimeHistogramViewModel getResponseTimeHistogramData(
            @RequestParam("applicationName") String applicationName,
            @RequestParam("serviceTypeName") String serviceTypeName,
            @RequestParam("from") long from,
            @RequestParam("to") long to) {
        final Range range = new Range(from, to);
        dateLimit.limit(range);

        Application application = applicationFactory.createApplicationByTypeName(applicationName, serviceTypeName);

        ApplicationTimeHistogramViewModel applicationTimeHistogramViewModel = mapService.selectResponseTimeHistogramData(application, range);

        return applicationTimeHistogramViewModel;
    }
    
    private TreeNode recursiveTree(List<List<Object>> list, String tid, String mRpc) {
		TreeNode tn = new TreeNode();
		List<String> tags = new ArrayList<>();
		List<List<Object>> childList = new ArrayList<>();
		for (List<Object> cList : list) {
			if (tid.equals(cList.get(6).toString())) {
				if (cList.get(4) == null) {
					tn.setTags(null);
					tn.setText(cList.get(10) + "");
					tn.setIcon("glyphicon glyphicon-info-sign");
					continue;
				} else {
					tn.setText(cList.get(10) + " (" + cList.get(11) + ")");
					tags.add(transferLongToDate(sdf, (long) cList.get(1)));
					tags.add(cList.get(14) + "ms");
					tags.add(cList.get(20) + " (" + cList.get(4) + ")");
					tags.add("方法名: " + cList.get(17) + " (" + cList.get(19) + ")");
					tn.setTags(tags);
				}
				if ("Tomcat Servlet Process".equals(cList.get(10).toString())) {
					tn.setIcon("glyphicon glyphicon-cloud");
					if (mRpc.equals(cList.get(11).toString())) {
						tn.setColor("#428bca");
						tn.setBackColor("#9eff7c");
					}
				}
				continue;
			}
			if (tid.equals(cList.get(7).toString())) {
				childList.add(cList);
			}
		}
		for (List<Object> child : childList) {
			TreeNode rl = recursiveTree(list, child.get(6).toString(), mRpc);
			tn.getNodes().add(rl);
		}
		if (CollectionUtils.isEmpty(tn.getNodes())) {
			tn.setNodes(null);
		}
		return tn;
	}
    private String transferLongToDate(SimpleDateFormat sdf, long millSec) {
		Date date = new Date(millSec);
		return sdf.format(date);
	}

}
